package cn.sell.open.client;


import cn.sell.comm.result.enums.ResultCode;
import cn.sell.comm.result.exceptions.BusinessException;
import cn.sell.open.config.ApplicationContextHelper;
import cn.sell.open.config.ApplicationProperty;
import cn.sell.open.container.ApiContainer;
import cn.sell.open.model.ApiModel;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName:ApiClient
 * @Description: TODO: Api请求客户端
 * @Author: Limi
 * @Contact : qq(2393839633),Email(13924223985@163.com)
 * @Date: 2020/3/25 17:10
 * @Version: 1.0
 **/
@Service
public class ApiClient {

    /**
     * 日志
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ApiClient.class);

    /**
     * jackson 序列化工具类
     */
    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    /**
     * Api本地容器
     */
    private final ApiContainer apiContainer;

    public ApiClient(ApiContainer apiContainer) {
        this.apiContainer = apiContainer;
    }


    @Resource
    private ApplicationProperty applicationProperty;

    /**
     * 验签
     *
     * @param params          请求参数
     * @param requestRandomId 请求随机标识（用于日志中分辨是否是同一次请求）
     * @param charset         请求编码
     * @param signType        签名格式
     * @author 码农猿
     */
    public void checkSign(Map<String, Object> params, String requestRandomId, String charset, String signType) {

        try {
            //校验签名开关
            if (!applicationProperty.getIsCheckSign()) {
                LOGGER.warn("【{}】>> 验签开关关闭", requestRandomId);
                return;
            }

            //map类型转换
            Map<String, String> map = new HashMap<>(params.size());
            for (String s : params.keySet()) {
                map.put(s, params.get(s).toString());
            }

            LOGGER.warn("【{}】 >> 验签参数 {}", requestRandomId, map);
//            boolean checkSign = AlipaySignature.rsaCheckV1(map, applicationProperty.getPublicKey(), charset, signType);
//            if (!checkSign) {
//                LOGGER.info("【{}】 >> 验签失败 >> params = {}", requestRandomId, JSON.toJSONString(params));
//                throw new BusinessException(ResultCode.PARAM_SIGN_INVALID);
//            }
            LOGGER.warn("【{}】 >> 验签成功", requestRandomId);

        } catch (Exception e) {
            LOGGER.error("【{}】 >> 验签异常 >> params = {}, error = {}",
                    requestRandomId, JSON.toJSONString(params), ExceptionUtils.getStackTrace(e));
            throw new BusinessException(ResultCode.SYSTEM_INNER_ERROR);

        }

    }


    /**
     * Api调用方法
     *
     * @param method          请求方法
     * @param requestRandomId 请求随机标识
     * @param content         请求体
     * @author 码农猿
     * @return
     */
    public Object invoke(String method, String requestRandomId, String content) throws Throwable {
        //获取api方法
        ApiModel apiModel = apiContainer.get(method);

        if (null == apiModel) {
            LOGGER.info("【{}】 >> API方法不存在 >> method = {}", requestRandomId, method);
            throw new BusinessException(ResultCode.INTERFACE_ADDRESS_INVALID);
        }

        //获得spring bean
        Object bean = ApplicationContextHelper.getBean(apiModel.getBeanName());
        if (null == bean) {
            LOGGER.warn("【{}】 >> API方法不存在 >> method = {}, beanName = {}", requestRandomId, method, apiModel.getBeanName());
            throw new BusinessException(ResultCode.INTERFACE_INNER_INVOKE_ERROR);
        }

        //处理业务参数
        // 忽略JSON字符串中存在，而在Java中不存在的属性
        JSON_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 设置下划线序列化方式
        JSON_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        Object result = JSON_MAPPER.readValue(content, Class.forName(apiModel.getParamName()));

        //校验参数
//        ValidateUtils.validate(result);

        //执行对应方法
        try {
            Object obj = apiModel.getMethod().invoke(bean, requestRandomId, result);
            return obj;
        } catch (Exception e) {
            if (e instanceof InvocationTargetException) {
                throw ((InvocationTargetException) e).getTargetException();
            }
            throw new BusinessException(ResultCode.SYSTEM_INNER_ERROR);
        }

    }
}
